using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using System.Text;
using System.Drawing.Printing;

namespace RandomRat {

	/// <summary>
	/// Summary description for PrintSets.
	/// </summary>
	public class PrintSets : System.Windows.Forms.Form {
		
		private string _sets = "";

		SetArguments setArgs;	// To be set when we know which ones we are using (Sample, Old or current)
		
		Thread workerThread;
		
		RandomSetGenerator rndSetGen;
		
		bool printPreview;

		ArrayList rndSets;

		SetArguments curSetArgs;
		ArrayList previouslyGenSets;
		ListBox sampleSet;
		SetArguments setArgsForPreviouslyGenSets;
		SetArguments setArgsForPreviouslyGenSample;

		bool printDiologOpen = false;

		#region WINVARS

		private System.Windows.Forms.ProgressBar progressBar1;
		private System.Windows.Forms.Label label4;
		private System.Windows.Forms.Button butOK;
		private System.Windows.Forms.PrintDialog printDialog1;
		private System.Windows.Forms.ErrorProvider errorProvider1;
		private System.Windows.Forms.Button butCancel;
		private System.Windows.Forms.GroupBox groupBox1;
		private System.Windows.Forms.RadioButton radioButton1;
		private System.Windows.Forms.RadioButton radioButton2;
		private System.Windows.Forms.RadioButton radioButton3;
		private System.Windows.Forms.Label label1;
		private System.Windows.Forms.Label label2;
		private NumberBox txtSets;
		private System.ComponentModel.Container components = null;

		#endregion

		public PrintSets(SetArguments curSetArgs, bool printPreview, string sets, int radioSelection, 
						 ListBox sampleSet, SetArguments setArgsForPreviouslyGenSample, 
						 ArrayList previouslyGenSets, SetArguments setArgsForPreviouslyGenSets) {
			
			InitializeComponent();
			
			this.txtSets.Text = sets;

			this.sampleSet = sampleSet;
			this.previouslyGenSets = previouslyGenSets;
			this.setArgsForPreviouslyGenSets = setArgsForPreviouslyGenSets;
			this.setArgsForPreviouslyGenSample = setArgsForPreviouslyGenSample;

			this.curSetArgs = curSetArgs;

			if (!(sampleSet.Items.Count > 0)) {
				radioButton1.Enabled = false;
			}

			if (previouslyGenSets == null || !(previouslyGenSets.Count > 0)) {
				radioButton2.Enabled = false;
				label1.Enabled = false;
			}

			if (radioSelection == 1 && radioButton1.Enabled) { // Sample
				radioButton1.Checked = true; 
			} else if (radioSelection == 2 && radioButton2.Enabled) { // Old set
				radioButton2.Checked = true;
			} else if (radioSelection == 3) { // Gen new
				radioButton3.Checked = true;
			}

			// Check if curent setArgs difer from that of the lastGenereatedSet
			label2.Text = "";
			if (sampleSet.Items.Count > 0 && setArgsForPreviouslyGenSample != null &&
				setArgsForPreviouslyGenSample.ToString() != curSetArgs.ToString()) {
				label2.Text = "( current criteria differs )";
			}
			
			label1.Text = "";
			if (previouslyGenSets != null && setArgsForPreviouslyGenSets != null &&
				setArgsForPreviouslyGenSets.ToString() != curSetArgs.ToString()) {
				label1.Text = "( current criteria differs )";
			}



			this.rndSetGen = new RandomSetGenerator();
			this.printPreview = printPreview;

			if (printPreview) {
				this.Text = "Print Preview";
			}
		}




		public ArrayList RndSetsGenerated {
			get { return rndSets; }
		}
		public string Sets {
			get { return _sets; }
		}

        int lastSetsToPrint = -1;
        private void preGenRndSets_UIWork()
        {
            lastSetsToPrint = int.Parse(txtSets.Text);
            progressBar1.Minimum = 1;
            progressBar1.Maximum = lastSetsToPrint + 1;
            progressBar1.Value = 1;
        }

        private void genRndSets_UIProgInc()
        {
            progressBar1.Value += 1;
        }

		// Called from doWork (via the WorkingThread)
		// Generates n rndSets while updating the progress bar
		private ArrayList genRndSets() {

            this.Invoke(new UIWork(preGenRndSets_UIWork));

            rndSets = new ArrayList();
            			
			int i;
            for (i = 0; i < lastSetsToPrint; i++)
            {
				rndSets.Add(rndSetGen.GetValidList(setArgs));
                this.Invoke(new UIWork(genRndSets_UIProgInc));
			}

			return rndSets;
		}

        private void preDoWorkUI() {
            butCancel.Enabled = false;
        }

        private void postDoWorkUI()
        {
            this.DialogResult = DialogResult.OK;
            this.Close();
        }

		// WorkerThreads base function
		public void doWork() {
			ArrayList rndSets = genRndSets();

            this.Invoke(new UIWork(preDoWorkUI));

			RandomSetPrinter setPrinter = new RandomSetPrinter(rndSets, setArgs, this);
			
			if (printPreview) {
				setPrinter.Print(RandomSetPrintMode.PrintPreview, this);
			} else {
                setPrinter.Print(RandomSetPrintMode.PrintWithDiolog, this);
			}

            this.Invoke(new UIWork(postDoWorkUI));
		}

		

		private void butOK_Click(object sender, System.EventArgs e) {
			// Check input

			if (butOK.Text == "OK" && (workerThread == null || !workerThread.IsAlive) && inputIsValid()) {
				bool generatingSets = false;
				RandomSetPrinter setPrinter = null;
				
				if (radioButton1.Checked) { // Print sample only
					// Set which setArgs to use	
					setArgs = setArgsForPreviouslyGenSample;
					
					// Make a string array and then wrap in an array list
					ArrayList rndSets = new ArrayList();
					string[] sampleSet = new string[this.sampleSet.Items.Count];
				
					int i = 0;
					foreach (string type in this.sampleSet.Items) {
						sampleSet[i++] = type;
					}
					rndSets.Add(sampleSet);

					// Instansheate printer object
					setPrinter = new RandomSetPrinter(rndSets, setArgs, this);

				} else if (radioButton2.Checked) { // Use old
					setArgs = setArgsForPreviouslyGenSets;
					setPrinter = new RandomSetPrinter(previouslyGenSets, setArgs, this);
				
				} else if (radioButton3.Checked) { // Generate new
					setArgs = curSetArgs;
					generatingSets = true;
					_sets = txtSets.Text;
				}

				// Disable everything (but cnacle) regardless
				butOK.Enabled = false;
				txtSets.Enabled = false;
				label1.Enabled = false;
				label2.Enabled = false;
				label4.Enabled = false;
				radioButton1.Enabled = false;
				radioButton2.Enabled = false;
				radioButton3.Enabled = false;


				if (!generatingSets) {
					if (printPreview) {
                        setPrinter.Print(RandomSetPrintMode.PrintPreview, this);
					} else {
                        setPrinter.Print(RandomSetPrintMode.PrintWithDiolog, this);
					}

					this.DialogResult = DialogResult.OK;
					this.Close();

				} else {
					this.Text = "Generating Sets...";

					// Generate new sets and do printing
					workerThread = new Thread(new ThreadStart(this.doWork));
					workerThread.Start();
				}
				
			}
		}

		
		private void butCancel_Click(object sender, System.EventArgs e) {
			if (workerThread != null && workerThread.IsAlive) {
				workerThread.Abort();
			}

			_sets = "";
			
			this.DialogResult = DialogResult.Cancel;
			this.Close();
		}


		#region INPUT VALIDATION

		private bool inputIsValid() {
			
			int sets = -1;

			bool setsIsInt = false;

			StringBuilder sbTxtSets = new StringBuilder();
			
			// Reset errors
			errorProvider1.SetError(txtSets, "");
			
			if (!txtSets.Enabled) return true; ////////// RETURNING
			
			// Check if input is an integer
			setsIsInt = isIntInput(txtSets);

			// set valuse
			if (setsIsInt) sets = int.Parse(txtSets.Text);

			bool isValid = true;

			// Check rules
			if (setsIsInt && sets < 1) {
				isValid = false;
				addError(sbTxtSets, "The number of sets must be greater than 0.");
			}

			// Add errors
			if (setsIsInt) errorProvider1.SetError(txtSets, sbTxtSets.ToString());
	

			if (!setsIsInt) {
				isValid = false;
			}

			return isValid;
		}

		private void addError(StringBuilder sbErrors, string error) {
			if (sbErrors.Length == 0) {
				sbErrors.Append(error);
			} else {
				sbErrors.Append("\n" + error);
			}
		}

		private bool isIntInput(TextBox textBox) {
			try {
				int val = int.Parse(textBox.Text);
			} catch {
				errorProvider1.SetError(textBox, "Please provide a value.");
				return false;
			}

			return true;
		}

		
		#endregion
		
		private void radioButton3_CheckedChanged(object sender, System.EventArgs e) {
			txtSets.Enabled = radioButton3.Checked;
			label4.Enabled = radioButton3.Checked;
		}


		#region UTIL

		protected override void Dispose( bool disposing ) {
			if( disposing )	{
				if(components != null) {
					components.Dispose();
				}

				if (workerThread != null && workerThread.IsAlive) {
					workerThread.Abort();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent() {
			this.progressBar1 = new System.Windows.Forms.ProgressBar();
			this.label4 = new System.Windows.Forms.Label();
			this.butOK = new System.Windows.Forms.Button();
			this.printDialog1 = new System.Windows.Forms.PrintDialog();
			this.errorProvider1 = new System.Windows.Forms.ErrorProvider();
			this.butCancel = new System.Windows.Forms.Button();
			this.groupBox1 = new System.Windows.Forms.GroupBox();
			this.label2 = new System.Windows.Forms.Label();
			this.label1 = new System.Windows.Forms.Label();
			this.radioButton3 = new System.Windows.Forms.RadioButton();
			this.radioButton2 = new System.Windows.Forms.RadioButton();
			this.radioButton1 = new System.Windows.Forms.RadioButton();
			this.txtSets = new RandomRat.NumberBox();
			this.groupBox1.SuspendLayout();
			this.SuspendLayout();
			// 
			// progressBar1
			// 
			this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
				| System.Windows.Forms.AnchorStyles.Right);
			this.progressBar1.Location = new System.Drawing.Point(6, 152);
			this.progressBar1.Name = "progressBar1";
			this.progressBar1.Size = new System.Drawing.Size(296, 23);
			this.progressBar1.TabIndex = 0;
			// 
			// label4
			// 
			this.label4.Location = new System.Drawing.Point(12, 16);
			this.label4.Name = "label4";
			this.label4.Size = new System.Drawing.Size(72, 16);
			this.label4.TabIndex = 20;
			this.label4.Text = "Sets to print:";
			// 
			// butOK
			// 
			this.butOK.Location = new System.Drawing.Point(148, 12);
			this.butOK.Name = "butOK";
			this.butOK.Size = new System.Drawing.Size(72, 23);
			this.butOK.TabIndex = 1;
			this.butOK.Text = "OK";
			this.butOK.Click += new System.EventHandler(this.butOK_Click);
			// 
			// errorProvider1
			// 
			this.errorProvider1.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink;
			// 
			// butCancel
			// 
			this.butCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
			this.butCancel.Location = new System.Drawing.Point(228, 12);
			this.butCancel.Name = "butCancel";
			this.butCancel.Size = new System.Drawing.Size(72, 23);
			this.butCancel.TabIndex = 2;
			this.butCancel.Text = "Cancel";
			this.butCancel.Click += new System.EventHandler(this.butCancel_Click);
			// 
			// groupBox1
			// 
			this.groupBox1.Controls.AddRange(new System.Windows.Forms.Control[] {
																					this.label2,
																					this.label1,
																					this.radioButton3,
																					this.radioButton2,
																					this.radioButton1});
			this.groupBox1.Location = new System.Drawing.Point(8, 43);
			this.groupBox1.Name = "groupBox1";
			this.groupBox1.Size = new System.Drawing.Size(292, 96);
			this.groupBox1.TabIndex = 21;
			this.groupBox1.TabStop = false;
			this.groupBox1.Text = "Use:";
			// 
			// label2
			// 
			this.label2.BackColor = System.Drawing.Color.Transparent;
			this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.label2.ForeColor = System.Drawing.Color.DarkRed;
			this.label2.Location = new System.Drawing.Point(103, 21);
			this.label2.Name = "label2";
			this.label2.Size = new System.Drawing.Size(124, 16);
			this.label2.TabIndex = 4;
			this.label2.Text = "( current criteria differs )";
			// 
			// label1
			// 
			this.label1.BackColor = System.Drawing.Color.Transparent;
			this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.label1.ForeColor = System.Drawing.Color.DarkRed;
			this.label1.Location = new System.Drawing.Point(126, 45);
			this.label1.Name = "label1";
			this.label1.Size = new System.Drawing.Size(124, 16);
			this.label1.TabIndex = 3;
			this.label1.Text = "( current criteria differs )";
			// 
			// radioButton3
			// 
			this.radioButton3.Location = new System.Drawing.Point(9, 69);
			this.radioButton3.Name = "radioButton3";
			this.radioButton3.Size = new System.Drawing.Size(200, 16);
			this.radioButton3.TabIndex = 2;
			this.radioButton3.Text = "New sets based on current criteria";
			this.radioButton3.CheckedChanged += new System.EventHandler(this.radioButton3_CheckedChanged);
			// 
			// radioButton2
			// 
			this.radioButton2.Location = new System.Drawing.Point(9, 45);
			this.radioButton2.Name = "radioButton2";
			this.radioButton2.Size = new System.Drawing.Size(127, 16);
			this.radioButton2.TabIndex = 1;
			this.radioButton2.Text = "Last generated sets";
			this.radioButton2.CheckedChanged += new System.EventHandler(this.radioButton3_CheckedChanged);
			// 
			// radioButton1
			// 
			this.radioButton1.Location = new System.Drawing.Point(9, 21);
			this.radioButton1.Name = "radioButton1";
			this.radioButton1.Size = new System.Drawing.Size(104, 16);
			this.radioButton1.TabIndex = 0;
			this.radioButton1.Text = "Current sample";
			this.radioButton1.CheckedChanged += new System.EventHandler(this.radioButton3_CheckedChanged);
			// 
			// txtSets
			// 
			this.txtSets.Location = new System.Drawing.Point(86, 13);
			this.txtSets.Name = "txtSets";
			this.txtSets.Size = new System.Drawing.Size(44, 20);
			this.txtSets.TabIndex = 22;
			this.txtSets.Text = "";
			// 
			// PrintSets
			// 
			this.AcceptButton = this.butOK;
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.CancelButton = this.butCancel;
			this.ClientSize = new System.Drawing.Size(308, 180);
			this.ControlBox = false;
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.txtSets,
																		  this.groupBox1,
																		  this.butCancel,
																		  this.butOK,
																		  this.label4,
																		  this.progressBar1});
			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
			this.MaximizeBox = false;
			this.Name = "PrintSets";
			this.ShowInTaskbar = false;
			this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
			this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
			this.Text = "Print Sets";
			this.groupBox1.ResumeLayout(false);
			this.ResumeLayout(false);

		}
		#endregion
		#endregion


	}



	public enum RandomSetPrintMode { PrintWithDiolog, PrintWithOutDiolog, PrintPreview } 

	public class RandomSetPrinter {
		System.Windows.Forms.Form parent;
		SetArguments setArgs;
		
		string[] lines;
		int curLine;

		bool printDiologOpen = false;

		public RandomSetPrinter(ArrayList rndSets, SetArguments setArgs, System.Windows.Forms.Form parent) {
			this.parent = parent;
			this.setArgs = setArgs;
			
			lines = getLines(rndSets, setArgs);
		}

		public bool PrintDiologOpen {
			get { return printDiologOpen; }
		}

		bool success;


        bool printHelperResult;
        public void printHelper(RandomSetPrintMode printMode)
        {
            success = false;

            PrintDocument pDocument = new PrintDocument();
            pDocument.BeginPrint += new PrintEventHandler(this.pd_BeginPrint);
            pDocument.PrintPage += new PrintPageEventHandler(this.pd_PrintPage);
            pDocument.EndPrint += new PrintEventHandler(this.pd_EndPrint);

            if (printMode == RandomSetPrintMode.PrintPreview)
            {
                PrintPreviewDialog ppDialog = new PrintPreviewDialog();
                ppDialog.Width = 380;
                ppDialog.Height = 475;

                ppDialog.Document = pDocument;

                printDiologOpen = true;
                ppDialog.ShowDialog(parent);
                printDiologOpen = false;

                ppDialog.Dispose();

            }
            else if (printMode == RandomSetPrintMode.PrintWithDiolog)
            {
                PrintDialog printDialog1 = new PrintDialog();
                printDialog1.UseEXDialog = true;

                printDialog1.AllowSelection = false;
                printDialog1.AllowSomePages = true;

                printDialog1.Document = pDocument;

                printDiologOpen = true;
                DialogResult result = printDialog1.ShowDialog();
                if (result == DialogResult.OK)
                {
                    printDiologOpen = false;
                    printDialog1.Document.Print();
                }

                printDialog1.Dispose();

            }
            else if (printMode == RandomSetPrintMode.PrintWithOutDiolog)
            {
                pDocument.Print();
            }

            pDocument.Dispose();

            printHelperResult = success;
        }

        private delegate void PrintHelperDelegate(RandomSetPrintMode printMode);

		public bool Print(RandomSetPrintMode printMode, Control hostControl) {
            if (hostControl.InvokeRequired)
            {
                hostControl.Invoke(new PrintHelperDelegate(printHelper), printMode);
            }
            else
            {
                printHelper(printMode);
            }
            return printHelperResult;
		}

        public bool Print(Control hostControl) { return Print(RandomSetPrintMode.PrintWithDiolog, hostControl); }


		
		public static string[] getLines(ArrayList rndSets, SetArguments setArgs) {
			int slots = setArgs.Slots;
			string[] types = setArgs.Types;
			int numOfsets = rndSets.Count;

			// printing paramiters
			int columCharWidth = 0;
			int colums = 0;
			int rows = 0;
			
			// Find larges (number of chars) type
			int largest = -1;
			foreach (string type in types) {
				if (largest == -1 || type.Length > largest) {
					largest = type.Length;
				}
			}

			// Determen columCharWidth
			if (largest <= 3) {
				columCharWidth = 5; 
			} else {
				columCharWidth = largest + 2;
			}

			// Determen number of coulums
			int totalCharWidth; // 75 chars
			
			// Allow for more effective use (of space) for far right colums
			if (largest <= 2) {
				totalCharWidth = 85;
			} else {
				totalCharWidth = 80;
			}
			//uhg
			totalCharWidth = 80;
			
			colums = totalCharWidth / columCharWidth;

			// Determen number of rows
			rows = rndSets.Count / colums;
			rows++;

			// Generate matrix of lists
			string[][][] sets = new string[rows][][];
			int row;
			int colum;
			int setNum = 0;
			for (row = 0; row < rows; row++) {
				sets[row] = new string[colums][];
				for (colum = 0; colum < colums; colum++) {
					if (setNum < numOfsets) {
						sets[row][colum] = (string[])rndSets[setNum];
						setNum++;
					} else {
						row = rows;
						colum = colums;
					}
				}
			}


			// Generate lines
			ArrayList lines = new ArrayList();
			for (row = 0; row < rows; row++) {
				// print xth of each set in row
				int x; // slot item
				for (x = 0; x < slots; x++) {
					StringBuilder sb = new StringBuilder();
					// Interate over each colum to append x type in each
					for (colum = 0; colum < colums; colum++) {
						if (sets[row][colum] != null) {
							sb.Append(sets[row][colum][x]); // Add type
							int nSpace = columCharWidth - sets[row][colum][x].Length; // calculate space left
							int cnt;
							for (cnt = 0; cnt < nSpace; cnt++) {
								sb.Append(" "); // Add spacees left
							}
						} else {
							break;
						}
					}
					if (sb.Length > 0){
						lines.Add(sb.ToString());
					}
				}

				// At the end of each row
				lines.Add("");
			}

			// Remove any blank lines at end
			while (((string)lines[lines.Count - 1]).Trim() == "") {
				lines.RemoveAt(lines.Count - 1);
			}


			string[] linesArray = new string[lines.Count];  // Last one is a blank line
			int i;
			for (i = 0; i < linesArray.Length; i++) {
				linesArray[i] = (string)lines[i];
			}
			
			return linesArray;
		}

		private string readLine() {
			if (curLine >= lines.Length) {
				return null;
			} else {
				return lines[curLine++];
			}
		}


		private void pd_BeginPrint(object sender, PrintEventArgs ev) {
			curLine = 0;
		}

		private void pd_EndPrint(object sender, PrintEventArgs ev) {
			success = true;
		}

		
		// The PrintPage event is raised for each page to be printed.
		private void pd_PrintPage(object sender, PrintPageEventArgs ev) {
			Font printFont = new Font("Courier New", 11f);
			float linesPerPage = 0;
			float yPos = 0;
			int count = 0;
			float leftMargin = 35.0f; //ev.MarginBounds.Left;
			float topMargin = 35.0f; //ev.MarginBounds.Top;
			string line = null;

			// Calculate the number of lines per page.
			linesPerPage = 55f; //ev.MarginBounds.Height / printFont.GetHeight(ev.Graphics);
			//linesPerPage += 2f; // Make room for extraspace and then set information

			// Print each line of the file.
			while(count < linesPerPage && ((line = readLine()) != null)) {
				if (line == "" && linesPerPage - count - 1 < setArgs.Slots) {
					count = (int)linesPerPage + 1; //JIC
				} else {
					yPos = topMargin + (count * printFont.GetHeight(ev.Graphics));
					ev.Graphics.DrawString(line, printFont, Brushes.Black, leftMargin, yPos, new StringFormat());
					count++;
				}
			}

			Font footFont = new Font("Courier New", 9f);
			yPos = topMargin + ((linesPerPage + 2f) * printFont.GetHeight(ev.Graphics));
			ev.Graphics.DrawString(DateTime.Now.ToString() + "   " + setArgs.ToString(), footFont, Brushes.Black, leftMargin, yPos, new StringFormat());
			

			// If more lines exist, print another page.
			if(line == null) {
				ev.HasMorePages = false;
			} else {
				ev.HasMorePages = true;
			}
		}




	}
}
